/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package data;

import java.awt.Dimension;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.ProgressMonitor;

import view.PlayerCanvas;
import view.admin.AdminFrame;

import com.enterprisedt.net.ftp.FTPClient;
import com.enterprisedt.net.ftp.FTPConnectMode;
import com.enterprisedt.net.ftp.FTPException;
import com.enterprisedt.net.ftp.FTPFile;
import com.enterprisedt.net.ftp.FTPMessageListener;
import com.enterprisedt.net.ftp.FTPProgressMonitor;
import com.enterprisedt.net.ftp.FTPTransferType;
import command.CommandFactory;

import config.Debug;
import config.Helper;
import config.Settings;

/**
 * 
 * @author Yang Wang <yang@digitalmenubox.com>
 */
public class FtpDownloader extends Downloader implements FTPMessageListener {

	private static FtpDownloader instance;
	private boolean showProgressWindow = true;
	private String source;
	private String dest;
	private int host = DIGITAL_MARKETING_BOX_SERVER;
	private String server;
	private String username;
	private String password;
	private FTPClient ftp;
	private List<String> ftpLog = new ArrayList<String>();
	private int serverNumber = 0;

	public boolean isShowProgressWindow() {
		return showProgressWindow;
	}

	@Override
	public void setShowProgressWindow(boolean showProgressWindow) {
		this.showProgressWindow = showProgressWindow;
	}

	public static FtpDownloader getInstance() {
		if (instance == null) {
			instance = new FtpDownloader();
		}
		return instance;
	}

	private FtpDownloader() {
		setupConnectionInfo();
	}

	public void connect() {
		this.ftp = new FTPClient();
		// com.enterprisedt.util.debug.Logger.setLevel(com.enterprisedt.util.debug.Level.DEBUG);
		try {
			ftp.setRemoteHost(this.server);
			ftp.setMessageListener(this);
			ftpLog = new ArrayList<String>();
			ftp.connect();
			ftp.login(this.username, this.password);
			ftp.setConnectMode(FTPConnectMode.PASV);
			ftp.setType(FTPTransferType.BINARY);
			// ftp.setTimeout(10);//TODO: have this setting in admin frame
			ftp.keepAlive();
			ftp.setDeleteOnFailure(true);
		} catch (IOException ex) {
			Debug.log("IOException: " + ex.getMessage() + "\n" + this.getFtpLog(), Debug.EXCEPTION_LEVEL);
		} catch (FTPException ex) {
			// Logger.getLogger(FtpDownloader.class.getName()).log(Level.SEVERE,
			// null, ex);
			Debug.log("FTPException: " + ex.getMessage() + "\n" + this.getFtpLog(), Debug.EXCEPTION_LEVEL);
		}
	}

	@Override
	public synchronized boolean download(String remoteFile, String downloadedPath, int server) {
		this.setHost(server);
		this.setShowProgressWindow(true);
		this.source = remoteFile;
		this.dest = downloadedPath;
		boolean downloadNeeded = false;
		File dFile = new File(this.dest);
		if (this.ftp == null || !this.ftp.connected()) {
			connect();
		}

		if (!this.ftp.connected()) {
			return false;
		}

		FTPDownloadProcessMonitorFrame pm = null;
		try {
			boolean fileExists = false;
			try {
				fileExists = ftp.exists(this.source);
			} catch (FTPException ex) {
				Debug.log("FTPException: " + ex.getMessage(), Debug.EXCEPTION_LEVEL);
				Debug.msg(ex.getMessage());
			}
			if (fileExists) {
				if (dFile.exists() && dFile.length() > 0) {
					FTPFile ftpFile;
					try {
						ftpFile = ftp.fileDetails(this.source);
						if (ftpFile.lastModified().getTime() > dFile.lastModified()) {
							downloadNeeded = true;
						}
						// System.out.println(ftpFile.lastModified());
						// System.out.println(new Date(dFile.lastModified()));
					} catch (ParseException ex) {
						Logger.getLogger(FtpDownloader.class.getName()).log(Level.SEVERE, null, ex);
					}
				} else {
					downloadNeeded = true;
				}

				if (downloadNeeded) {
					long fileSize = ftp.size(this.source);
					// System.out.print("file size: " + fileSize/1000 + " ");
					if (this.showProgressWindow) {
						pm = new FTPDownloadProcessMonitorFrame(new File(this.dest).getName(), fileSize);
						PlayerCanvas.getInstance().getpLayout().add(pm, PlayerCanvas.getInstance().getpLayout().POPUP_LAYER);
						PlayerCanvas.getInstance().getpLayout().moveToFront(pm);
						ftp.setProgressMonitor(pm, 1);
					}

					File destFile = new File(this.dest);
					if (destFile.getParentFile() != null) {
						destFile.getParentFile().mkdirs();
					}
					ftp.get(this.dest, this.source);
				} else {
					// System.out.println("There is no need to download this file again - "
					// + dest.toString());
					return true;
				}
			} else {
				Debug.log("Download Failed: Remote File " + this.source + " does not exist.", Debug.WARNING_LEVEL);
				Debug.msg("Download Failed: Remote File " + this.source + " does not exist.");
				return false;
			}

		} catch (IOException ex) {
			// delete the file we are trying to download, if exception occured
			// during download
			File resource = new File(this.dest);
			resource.delete();
			// Logger.getLogger(FtpDownloader.class.getName()).log(Level.SEVERE,
			// null, ex);
			Debug.log(FtpDownloader.class.getName() + ": " + ex.getMessage() + "\n" + this.getFtpLog(), Debug.WARNING_LEVEL);
			if (ex.getMessage().contains("Broken pipe") || ex.getMessage().contains("unexpectedly closed")) {
				CommandFactory.getInstance().restartPlayer();
			}
			return false;
		} catch (FTPException ex) {
			// delete the file we are trying to download, if exception occured
			// during download
			File resource = new File(this.dest);
			resource.delete();
			// Logger.getLogger(FtpDownloader.class.getName()).log(Level.SEVERE,
			// null, ex);
			Debug.log(FtpDownloader.class.getName() + ": " + ex.getMessage() + " reply code: " + ex.getReplyCode() + "\n" + this.getFtpLog(),
					Debug.WARNING_LEVEL);
			if (ex.getMessage().contains("Broken pipe") || ex.getMessage().contains("unexpectedly closed")) {
				CommandFactory.getInstance().restartPlayer();
			}
			return false;
		} finally {
			try {
				ftp.quit();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (FTPException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if (pm != null) {
				PlayerCanvas.getInstance().getpLayout().remove(pm);
				PlayerCanvas.getInstance().getpLayout().revalidate();
				PlayerCanvas.getInstance().getpLayout().repaint();
			}
		}

		// if we detect that a file is 0kb, then this file needs redownload
		if (dFile.length() == 0) {
			dFile.delete();
			Debug.msg((dFile.toString() + " is downloaded with file size 0.\nThis file will be deleted and redownloaded."));
			return false;
		}

		if (downloadNeeded) {
			Debug.msg("File downloaded: " + this.source + " (" + dFile.length() / 1024 + "kb)");
			Debug.log("File downloaded: " + this.source + " (" + dFile.length() / 1024 + "kb)");
		}
		return true;
	}

	private void setupConnectionInfo() {
		switch (host) {
		case Downloader.DIGITAL_AUTOBOX_BOX_SERVER:
			this.server = config.Settings.getAutoboxFtpHost();
			this.username = config.Settings.getAutoboxFtpUser();
			this.password = config.Settings.getAutoboxFtpPassword();
			break;
		case Downloader.DIGITAL_REAL_ESTATE_BOX_SERVER:
			this.server = config.Settings.getRealEstateFtpHost();
			this.username = config.Settings.getRealEstateFtpUser();
			this.password = config.Settings.getRealEstateFtpPassword();
			break;
		case Downloader.DIGITAL_REAL_ESTATE_BOX_AGENT_SERVER:
			// uses the marketingbox ftp.
		default:
			this.server = config.Settings.getWebtoolFtpHost();
			this.username = config.Settings.getWebtoolFtpUser();
			this.password = config.Settings.getWebtoolFtpPassword();
		}
	}

	public void setHost(int host) {
		switch (host) {
		case Downloader.DIGITAL_MARKETING_BOX_SERVER:
			this.host = Downloader.DIGITAL_MARKETING_BOX_SERVER;
			break;
		case Downloader.DIGITAL_AUTOBOX_BOX_SERVER:
			this.host = Downloader.DIGITAL_AUTOBOX_BOX_SERVER;
			break;
		case Downloader.DIGITAL_REAL_ESTATE_BOX_SERVER:
			this.host = Downloader.DIGITAL_REAL_ESTATE_BOX_SERVER;
			break;
		default:
			this.host = Downloader.DIGITAL_MARKETING_BOX_SERVER;
		}
		this.setupConnectionInfo();
	}

	private void disconnect() {
		try {
			this.ftp.quitImmediately();
		} catch (IOException ex) {
			Logger.getLogger(FtpDownloader.class.getName()).log(Level.SEVERE, null, ex);
		} catch (FTPException ex) {
			Logger.getLogger(FtpDownloader.class.getName()).log(Level.SEVERE, null, ex);
		}
	}

	public static void main(String[] args) throws InterruptedException {
		System.out.println(Downloader.createDownloader().download(Settings.BOXDATA_HTTP_DIRECTORY + "digitalautobox.com/bmwbavaria/carimages/7_2.jpg",
				"7_2.jpg", DIGITAL_AUTOBOX_BOX_SERVER));
	}

	public String getFtpLog() {
		StringBuilder sb = new StringBuilder();
		for (String log : ftpLog) {
			sb.append(log).append("\n");
		}
		return sb.toString();
	}

	@Override
	public void logCommand(String string) {
		this.ftpLog.add("CMD: " + string);
	}

	@Override
	public void logReply(String string) {
		this.ftpLog.add("RPL: " + string);
	}
}

class FTPDownloadProcessMonitor extends ProgressMonitor implements FTPProgressMonitor {

	private long size;
	private static int unitSize = 1024;

	public FTPDownloadProcessMonitor(String filename, long size) {
		super(AdminFrame.getInstance(), "Downloading " + filename, "test", 0, (int) size / unitSize);
		this.size = size / unitSize;
		this.setProgress(0);

	}

	@Override
	public void bytesTransferred(long l) {
		l = l / unitSize;
		this.setProgress((int) l);
		this.setNote(l + "/" + size + "KBs transfered");
	}
}

class FTPDownloadProcessMonitorFrame extends JPanel implements FTPProgressMonitor, WindowListener {

	private String filename;
	private JProgressBar progressBar;
	private long size;
	private static int unitSize = 1024;

	private static final int DOWNLOAD_BAR_HEIGHT = 25;

	public FTPDownloadProcessMonitorFrame(String filename, long size) {
		// super("Downloading updates");
		// this.addWindowListener(this);
		// this.setUndecorated(true);
		this.setOpaque(false);
		this.filename = filename;
		progressBar = new JProgressBar(0, (int) (size / unitSize));
		progressBar.setValue(0);
		progressBar.setStringPainted(true);
		progressBar.setPreferredSize(new Dimension(Helper.scale(Settings.getPlayerWidth()), DOWNLOAD_BAR_HEIGHT));
		progressBar.setBorder(null);
		progressBar.setOpaque(false);
		this.add(progressBar);
		this.setBounds(0, (Helper.scale(Settings.getPlayerHeight())) - DOWNLOAD_BAR_HEIGHT, Helper.scale(Settings.getPlayerWidth()), DOWNLOAD_BAR_HEIGHT);
		// this.pack();
		// this.setVisible(true);
		// this.setAlwaysOnTop(true);
		// PlayerCanvas.getInstance().setAlwaysToFront(false);
		// this.toFront();
	}

	@Override
	public void bytesTransferred(long l) {
		// l = l / unitSize;
		progressBar.setString("Downloading " + filename + ": " + l / unitSize + "KBs/" + progressBar.getMaximum() + "KBs");
		progressBar.setValue((int) l / unitSize);
		if ((int) (l / unitSize) >= progressBar.getMaximum()) {
			PlayerCanvas.getInstance().setAlwaysOnTop(true);
		}
		// progressBar.(l + "/" + size + "KBs transfered");
	}

	@Override
	public void windowOpened(WindowEvent e) {
		// this.toFront();
	}

	@Override
	public void windowClosing(WindowEvent e) {
	}

	@Override
	public void windowClosed(WindowEvent e) {
	}

	@Override
	public void windowIconified(WindowEvent e) {
		// this.toFront();
	}

	@Override
	public void windowDeiconified(WindowEvent e) {
	}

	@Override
	public void windowActivated(WindowEvent e) {
	}

	@Override
	public void windowDeactivated(WindowEvent e) {
		// this.toFront();
	}
}
